
package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/satori/go.uuid"
	"log"
)

type ClientManager struct {
	clients    map[*Client]bool
	broadcast  chan []byte
	register   chan *Client
	unregister chan *Client
}

type Client struct {
	id   string
	conn *websocket.Conn
	send chan []byte
}

type Message struct {
	Sender    string `json:"sender,omitempty"`
	Recipient string `json:"recipient,omitempty"`
	Content   string `json:"content,omitempty"`
}

var manager = ClientManager{
	broadcast:  make(chan []byte),
	register:   make(chan *Client),
	unregister: make(chan *Client),
	clients:    make(map[*Client]bool),
}

func (manager *ClientManager) start() {
	for {
		select {
		case client := <-manager.register:
			manager.clients[client] = true
			jsonMessage, _ := json.Marshal(&Message{Content: "/A new client has connected."})
			manager.send(jsonMessage, client)
		case client := <-manager.unregister:
			if _, ok := manager.clients[client]; ok {
				close(client.send)
				delete(manager.clients, client)
				jsonMessage, _ := json.Marshal(&Message{Content: "/A client has disconnected."})
				manager.send(jsonMessage, client)
			}
		case message := <-manager.broadcast:
			for client := range manager.clients {
				log.Println(len(manager.clients), client.id)
				select {
				case client.send <- message:
				default:
					close(client.send)
					delete(manager.clients, client)
				}
			}
		}
	}
}

func (manager *ClientManager) send(message []byte, ignore *Client) {
	for client := range manager.clients {
		if client != ignore {
			client.send <- []byte(string(message) + "---" + ignore.id )
		}
	}
}

func (client *Client) read() {
	defer func() {
		manager.unregister <- client
		client.conn.Close()
	}()

	for {
		_, message, err := client.conn.ReadMessage()
		if err != nil {
			manager.unregister <- client
			client.conn.Close()
			break
		}
		jsonMessage, _ := json.Marshal(&Message{Sender: client.id, Content: string(message)})
		manager.broadcast <- jsonMessage
	}
}

func (client *Client) write() {
	defer func() {
		client.conn.Close()
	}()

	for {
		select {
		case message, ok := <-client.send:
			if !ok {
				client.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			client.conn.WriteMessage(websocket.TextMessage, message)
		}
	}
}

func main() {

	fmt.Println("Starting application...13346...")
	go manager.start()
	http.HandleFunc("/ws", wsPage)
	http.ListenAndServe(":13346", nil)

}

func wsPage(res http.ResponseWriter, req *http.Request) {
	conn, error := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(res, req, nil)
	if error != nil {
		http.NotFound(res, req)
		return
	}

	uuids, _ := uuid.NewV4()
	client := &Client{id: uuids.String(), conn: conn, send: make(chan []byte)}

	manager.register <- client

	go client.read()
	go client.write()
}
